package com.ramnight.unify.kit;

import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;

import org.apache.commons.beanutils.BeanUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@SuppressWarnings({ "unchecked", "rawtypes" })
public class UnifyConverter {
	public static UnifyJsonParser jsonParser = new UnifyJsonParser() {
		@Override
		public String toJson(Object obj) {
			return JSON.toJSONString(obj);
		}

		// 按map key排序生成json
		@Override
		public String toJsonSorted(Object obj) {
			return JSON.toJSONString(obj, SerializerFeature.MapSortField);
		}

		@Override
		public Map<String, Object> fromJsonToMap(String json) {
			return JSON.parseObject(json);
		}

		@Override
		public <T> T fromJsonToBean(String json, Class<T> clazz) {
			return JSON.parseObject(json, clazz);
		}

		@Override
		public <T> List<T> fromJsonToList(String json, Class<T> clazz) {
			return JSON.parseArray(json, clazz);
		}
	};

	public static void setJsonParser(UnifyJsonParser parser) {
		jsonParser = parser;
	}

	public static Long toLong(Object obj) {
		if (obj == null || "".equals(obj)) {
			return null;
		}
		if (obj instanceof Long) {
			return (Long) obj;
		}
		if (obj instanceof BigInteger) { // 支持JDBC
			return ((BigInteger) obj).longValue();
		}
		try {
			return Long.parseLong(String.valueOf(obj));
		} catch (Exception e) {
			log.error("to long error, obj:{}", obj, e);
			return null;
		}
	}

	public static Long toLong(Object obj, Long def) {
		return nullToDefault(toLong(obj), def);
	}

	public static String toJson(Object obj) {
		if (obj instanceof String) {
			return (String) obj;
		}
		return jsonParser.toJson(obj);
	}

	public static String toJson(Object obj, String def) {
		return nullToDefault(toJson(obj), def);
	}

	public static String toString(Object obj) {
		if (obj == null) {
			return null;
		}
		if (obj instanceof String) {
			return (String) obj;
		}
		return String.valueOf(obj);
	}

	public static String toString(Object obj, String def) {
		return nullToDefault(toJson(obj), def);
	}

	public static Integer toInteger(Object obj) {
		if (obj == null || "".equals(obj)) {
			return null;
		}
		if (obj instanceof Integer) {
			return (Integer) obj;
		}
		try {
			return Integer.parseInt(String.valueOf(obj));
		} catch (Exception e) {
			log.error("to integer error, obj:{}", obj, e);
			return null;
		}
	}

	public static Integer toInteger(Object obj, Integer def) {
		return nullToDefault(toInteger(obj), def);
	}

	public static Double toDouble(Object obj) {
		if (obj == null || "".equals(obj)) {
			return null;
		}
		if (obj instanceof Double) {
			return (Double) obj;
		}
		try {
			return Double.parseDouble(String.valueOf(obj));
		} catch (Exception e) {
			log.error("to double error, obj:{}", obj, e);
			return null;
		}
	}

	public static Double toDouble(Object obj, Double def) {
		return nullToDefault(toDouble(obj), def);
	}

	public static Boolean toBoolean(Object obj) {
		if (obj == null || "".equals(obj)) {
			return null;
		}
		if (obj instanceof Boolean) {
			return (Boolean) obj;
		}
		String str = String.valueOf(obj);
		if (str.equalsIgnoreCase("true")) {
			return Boolean.TRUE;
		} else if (str.equalsIgnoreCase("false")) {
			return Boolean.FALSE;
		}
		return null;
	}

	public static Boolean toBoolean(Object obj, Boolean def) {
		return nullToDefault(toBoolean(obj), def);
	}

	public static UnifyMap toUnifyMap(Object obj) {
		if (obj == null || "".equals(obj)) {
			return null;
		}
		if (obj instanceof UnifyMap) {
			return (UnifyMap) obj;
		}
		if (obj instanceof Map) {
			UnifyMap map = UnifyMap.of();
			Map tempMap = ((Map) obj);
			if (tempMap.size() == 0) {
				return UnifyMap.empty();
			}

			Object keySample = tempMap.keySet().iterator().next();
			if (keySample instanceof String) {
				return UnifyMap.ofMap(tempMap);
			}

			for (Entry<?, ?> e : ((Map<?, ?>) obj).entrySet()) {
				String key = toString(e.getKey());
				map.put(key, e.getValue());
			}
			return map;
		}
		if (obj instanceof String) {
			return UnifyMap.ofJson((String) obj);
		}
		if (obj instanceof Object) {
			return UnifyMap.ofBean(obj);
		}
		log.error("unsupport convert toUnifyMap({})", obj);
		return null;
	}

	public static UnifyMap toUnifyMap(Object obj, UnifyMap def) {
		return nullToDefault(toUnifyMap(obj), def);
	}

	public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) {
		try {
			if (clazz == map.getClass()) {
				return (T) map;
			}

			if (Map.class.isAssignableFrom(clazz)) {
				T instance = clazz.getDeclaredConstructor().newInstance();
				((Map<String, Object>) instance).putAll(map);
				return instance;
			}

			// pb暂不支持，thrift如需要自行修改
			// if (GeneratedMessageV3.class.isAssignableFrom(clazz)) {
			// return jsonParser.fromJsonToBean(jsonParser.toJson(map), clazz);
			// }

			T instance = clazz.getDeclaredConstructor().newInstance();
			// 其他key、value形式不支持
			BeanUtils.populate(instance, map);
			return instance;
		} catch (Exception e) {
			log.error("map to bean error, map:{}", map);
			return null;
		}
	}

	private static List toNewList(List list, Class clazz, Function<Object, Object> func) {
		List result = Lists.newArrayList();
		for (int i = 0; i < list.size(); i++) {
			if (!clazz.isInstance(list.get(i))) {
				result.add(func.apply(list.get(i)));
			} else {
				result.add(list.get(i));
			}
		}
		return result;
	}

	public static List toListDirect(Object obj) {
		return obj == null ? null : (List) obj;
	}

	public static Map toMapDirect(Object obj) {
		return obj == null ? null : (Map) obj;
	}

	// obj本身会被转为目标格式
	public static <T> List<T> toList(Object obj, Class<T> clazz) {
		if (obj == null || "".equals(obj)) {
			return null;
		}

		if (obj instanceof List) {
			List list = (List) obj;
			if (list.size() == 0) {
				return Collections.emptyList();
			}

			if (clazz == Long.class) { // json反序列化，会导致List<Long>中存在Integer
				return toNewList(list, clazz, UnifyConverter::toLong);
			}

			if (clazz == Integer.class) {
				return toNewList(list, clazz, UnifyConverter::toInteger);
			}

			if (clazz == Double.class) {
				return toNewList(list, clazz, UnifyConverter::toDouble);
			}

			// json序列化可能导致数字出现问题，放到前面去过滤
			if (clazz.isInstance(list.get(0))) {
				// TODO List<子类> 可能会有问题，比如map
				return list;
			}

			if (clazz == Boolean.class) { // json反序列化，会导致List<Long>中存在Integer
				return toNewList(list, clazz, UnifyConverter::toBoolean);
			}

			if (clazz == String.class) {
				return toNewList(list, clazz, UnifyConverter::toString);
			}

			if (clazz == UnifyMap.class) {
				return toNewList(list, clazz, UnifyConverter::toUnifyMap);
			}

			// tobean
			if (!Map.class.isAssignableFrom(clazz) && !Collection.class.isAssignableFrom(clazz)) {
				List<T> result = Lists.newArrayList();
				for (Object m : list) {
					result.add(toBean(m, clazz));
				}
				return result;
			}
		} else if (obj instanceof String) {
			return jsonParser.fromJsonToList((String) obj, clazz);
		} else if (obj instanceof Collection) {
			return Lists.newArrayList((Collection) obj);
		}
		log.error("unsupport convert toList({}, {})", obj, clazz);
		return null;
	}

	public static <T> List<T> toList(Object obj, Class<T> clazz, List<T> def) {
		return nullToDefault(toList(obj, clazz), def);
	}

	public static <T> T toBean(Object obj, Class<T> clazz) {
		if (obj == null || "".equals(obj)) {
			return null;
		}

		// 支持object
		if (clazz.isInstance(obj)) {
			return (T) obj;
		}

		if (obj instanceof Map) {
			// 其他key-value类型不支持
			return UnifyConverter.mapToBean((Map<String, Object>) obj, clazz);
		}

		if (obj instanceof String) {
			return jsonParser.fromJsonToBean((String) obj, clazz);
		}
		log.error("unsupport convert toBean({}, {})", obj, clazz);
		return null;
	}

	public static <T> T toBean(Object obj, Class<T> clazz, T def) {
		return nullToDefault(toBean(obj, clazz), def);
	}

	public static interface UnifyJsonParser {
		String toJson(Object obj);

		String toJsonSorted(Object obj);

		Map<String, Object> fromJsonToMap(String json);

		<T> T fromJsonToBean(String json, Class<T> clazz);

		<T> List<T> fromJsonToList(String json, Class<T> clazz);
	}

	public static <T> T nullToDefault(T t, T def) {
		return t == null ? def : t;
	}
}
